/*
Author: Surpass-HC
Source: https://gitee.com/hxchuan000/clpc-1
Mail: surpass-hc@qq.com
*/
#include <cycleQueHc.h>

// check total len, if que is full, realloc * 2.
static int cycleQueChkReTotal(cycleQueRoot *cqptr)
{
    char *tmpptr;
    size_t tmpTotal;
    if (cqptr->used_nbr < cqptr->total_nbr)
    {
        return 0;
    }
    else if (cqptr->used_nbr > cqptr->total_nbr)
    {
        logdebugc("used_nbr > total_nbr:%ld, %ld", cqptr->used_nbr, cqptr->total_nbr);
        return -2;
    }
    tmpTotal = cqptr->total_nbr * 2;
    if (tmpTotal == 0)
    {
        tmpTotal = CYCLE_QUE_INIT_SIZE;
    }
    if (tmpTotal >= CYCLE_QUE_WARN_SIZE)
    {
        logdebugc("size warn:%ld", tmpTotal);
    }
    logdebugc("need realloc:used_len=%ld, t=%ld, n=%ld", cqptr->used_nbr, cqptr->total_nbr, tmpTotal);
    tmpptr = (char *)realloc(cqptr->ptr, cqptr->element_size * tmpTotal);
    if (!tmpptr)
    {
        logdebugc("alloc fail:%ld", tmpTotal);
        return -1;
    }
    cqptr->ptr = tmpptr;
    cqptr->total_nbr = tmpTotal;
    if (cqptr->used_nbr == 0)
    {
        return 1;
    }
    if (cqptr->wid > cqptr->rid)
    {
        return 1;
    }
    size_t tmpCpNbr = cqptr->used_nbr - cqptr->rid;
    memcpy(cqptr->ptr + (cqptr->total_nbr - tmpCpNbr) * cqptr->element_size,
           cqptr->ptr + cqptr->rid * cqptr->element_size,
           tmpCpNbr * cqptr->element_size);
    cqptr->rid = cqptr->total_nbr - tmpCpNbr;
    // logdebugc("r=%ld,w=%ld,len=%ld,t=%ld", cqptr->rid, cqptr->wid, cqptr->len, cqptr->total);
    // for(long i = 0; i < cqptr->len; i++)
    // {
    //     long tmpid = (i + cqptr->rid) % cqptr->total;
    //     logdebugc("val[%ld]%p", tmpid, cqptr->ptr[tmpid]);
    // }
    return 1;
}
static void cycleQueLock(cycleQueRoot *cqptr)
{
    if (cqptr->withmt)
    {
        pthread_mutex_lock(&cqptr->mut);
    }
}
static void cycleQueUnlock(cycleQueRoot *cqptr)
{
    if (cqptr->withmt)
    {
        pthread_mutex_unlock(&cqptr->mut);
    }
}

int cycleQueInit(cycleQueRoot *cqptr, __compar_fn_t cfgcmp, size_t cfgElemsize, int useMutex)
{
    if (!cqptr)
    {
        logerrorc("");
        return -1;
    }
    memset(cqptr, 0, sizeof(cycleQueRoot));
    cqptr->cmpfn = cfgcmp;
    cqptr->withmt = useMutex;
    cqptr->element_size = cfgElemsize;
    pthread_mutex_init(&cqptr->mut, NULL);
    return 0;
}

int cycleQueFree(cycleQueRoot *cqptr)
{
    int tmpflag = 1;
    if (!cqptr)
    {
        logerrorc("");
        return -1;
    }
    if (cqptr->used_nbr > 0)
    {
        logerrorc("que have data, not free! pull data first!");
        return -1;
    }
    cycleQueLock(cqptr);
    do
    {
        if (cqptr->ptr)
        {
            free(cqptr->ptr);
            cqptr->ptr = NULL;
        }
        cqptr->used_nbr = 0;
        cqptr->rid = 0;
        cqptr->wid = 0;
        cqptr->total_nbr = 0;
        // cqptr->elemFreeFun = NULL;
        cqptr->cmpfn = NULL;
    } while (0);
    cycleQueUnlock(cqptr);
    if (tmpflag >= 0)
    {
        pthread_mutex_destroy(&cqptr->mut);
        memset(cqptr, 0, sizeof(cycleQueRoot));
    }
    return tmpflag;
}

size_t cycleQueSize(cycleQueRoot *cqptr)
{
    if (!cqptr)
    {
        logerrorc("");
        return -1;
    }
    return cqptr->used_nbr;
}

int cycleQuePushBack(cycleQueRoot *cqptr, void *ptr)
{
    int tmpflag = 1;
    if (!(cqptr && ptr))
    {
        logerrorc("");
        return -1;
    }
    cycleQueLock(cqptr);
    do
    {
        if (cycleQueChkReTotal(cqptr) < 0)
        {
            logerrorc("");
            tmpflag = -2;
            break;
        }
        memcpy(cqptr->ptr + cqptr->wid * cqptr->element_size, ptr, cqptr->element_size);
        cqptr->wid = CYCLE_ADD(cqptr->wid, 1, cqptr->total_nbr);
        cqptr->used_nbr++;
    } while (0);
    cycleQueUnlock(cqptr);
    return tmpflag;
}

int cycleQuePushFront(cycleQueRoot *cqptr, void *ptr)
{
    int tmpflag = 1;
    if (!cqptr)
    {
        logerrorc("");
        return -1;
    }
    cycleQueLock(cqptr);
    do
    {
        if (cycleQueChkReTotal(cqptr) < 0)
        {
            logerrorc("");
            tmpflag = -2;
            break;
        }
        cqptr->rid = CYCLE_ADD(cqptr->rid, -1, cqptr->total_nbr);
        memcpy(cqptr->ptr + cqptr->rid * cqptr->element_size, ptr, cqptr->element_size);
        cqptr->used_nbr++;
    } while (0);
    cycleQueUnlock(cqptr);
    return tmpflag;
}

int cycleQuePullFront(cycleQueRoot *cqptr, void *optr)
{
    int tmpflg = 0;
    if (!(cqptr && optr))
    {
        logerrorc("");
        return tmpflg;
    }
    cycleQueLock(cqptr);
    do
    {
        if (cqptr->used_nbr < 1ul)
        {
            break;
        }
        memcpy(optr, cqptr->ptr + cqptr->rid * cqptr->element_size, cqptr->element_size);
        cqptr->rid = CYCLE_ADD(cqptr->rid, 1, cqptr->total_nbr);
        cqptr->used_nbr--;
        tmpflg = 1;
    } while (0);
    cycleQueUnlock(cqptr);
    return tmpflg;
}

int cycleQuePullBack(cycleQueRoot *cqptr, void *optr)
{
    int tmpflg = 0;
    if (!(cqptr && optr))
    {
        logerrorc("");
        return tmpflg;
    }
    cycleQueLock(cqptr);
    do
    {
        if (cqptr->used_nbr < 1ul)
        {
            break;
        }
        cqptr->wid = CYCLE_ADD(cqptr->wid, -1, cqptr->total_nbr);
        memcpy(optr, cqptr->ptr + cqptr->wid * cqptr->element_size, cqptr->element_size);
        cqptr->used_nbr--;
        tmpflg = 1;
    } while (0);
    cycleQueUnlock(cqptr);
    return tmpflg;
}

static int cycleQueZeroBegin(cycleQueRoot *cqptr)
{
    int tmpflag = 1;
    if (!cqptr)
    {
        logerrorc("");
        return -1;
    }
    // cycleQueLock(cqptr);
    do
    {
        if (cqptr->rid == 0)
        {
            break;
        }
        else if (cqptr->rid < cqptr->wid)
        {
            for (int i = 0; i < cqptr->wid - cqptr->rid; i++)
            {
                memcpy(cqptr->ptr + i * cqptr->element_size, cqptr->ptr + (cqptr->rid + i) * cqptr->element_size, cqptr->element_size);
            }
            cqptr->rid = 0;
            cqptr->wid = cqptr->wid - cqptr->rid;
        }
        else
        {
            char *tmpptr = (char *)malloc(cqptr->element_size * cqptr->total_nbr);
            if (!tmpptr)
            {
                logdebugc("");
                tmpflag = -1;
                break;
            }
            for (int i = 0; i < cqptr->total_nbr - cqptr->rid; i++)
            {
                memcpy(tmpptr + i * cqptr->element_size, cqptr->ptr + (cqptr->rid + i) * cqptr->element_size, cqptr->element_size);
            }
            for (int i = 0; i < cqptr->wid; i++)
            {
                memcpy(tmpptr + (cqptr->total_nbr - cqptr->rid + i) * cqptr->element_size,
                       cqptr->ptr + (i)*cqptr->element_size, cqptr->element_size);
            }
            free(cqptr->ptr);
            cqptr->ptr = tmpptr;
            cqptr->rid = 0;
            cqptr->wid = cqptr->used_nbr;
        }
    } while (0);
    // cycleQueUnlock(cqptr);
    return tmpflag;
}

int cycleQueSort(cycleQueRoot *cqptr)
{
    if (!cqptr)
    {
        logerrorc("");
        return -1;
    }
    if (!cqptr->cmpfn)
    {
        logdebugc("no cmp fn");
        return -1;
    }
    cycleQueLock(cqptr);
    cycleQueZeroBegin(cqptr);
    qsort(cqptr->ptr, cqptr->used_nbr, cqptr->element_size, cqptr->cmpfn);
    cycleQueUnlock(cqptr);
    return 1;
}

size_t cycleQueSearch(cycleQueRoot *cqptr, void *findKey)
{
    if (!cqptr)
    {
        logerrorc("");
        return -1;
    }
    if (!cqptr->cmpfn)
    {
        logdebugc("no cmp fn");
        return -1;
    }
    cycleQueLock(cqptr);
    size_t tmpid = -1;
    void *tmpptr = bsearch(findKey, cqptr->ptr, cqptr->used_nbr, cqptr->element_size, cqptr->cmpfn);
    // logdebugc("%p,%p,%ld", tmpptr, cqptr->ptr, cqptr->used_nbr);
    if (tmpptr)
    {
        tmpid = ((size_t)tmpptr - (size_t)cqptr->ptr) / cqptr->element_size;
    }
    cycleQueUnlock(cqptr);
    return tmpid;
}